<?php

namespace App\Services\Finance;

use InvalidArgumentException;

class TaxCalculator
{
    /**
     * 个税累进税率表（灵活用工）。
     *
     * 自 PHP 7.4 起支持使用数字字面量下划线，仅用于增强可读性（如 1_0000 表示 10000）。
     *
     * 每个元素包含：
     * - threshold: 当前级别的累计预扣预缴应纳税所得额上限（null 表示无上限）
     * - rate: 预扣率
     * - quick_deduction: 速算扣除数
     *
     * @var array<int, array<string, float|null>>
     */
    private array $taxBrackets = [
        [
            'threshold' => 3_6000.0,
            'rate' => 0.03,
            'quick_deduction' => 0.0,
        ],
        [
            'threshold' => 14_4000.0,
            'rate' => 0.10,
            'quick_deduction' => 2_520.0,
        ],
        [
            'threshold' => 30_0000.0,
            'rate' => 0.20,
            'quick_deduction' => 1_6920.0,
        ],
        [
            'threshold' => 42_0000.0,
            'rate' => 0.25,
            'quick_deduction' => 3_1920.0,
        ],
        [
            'threshold' => 66_0000.0,
            'rate' => 0.30,
            'quick_deduction' => 5_2920.0,
        ],
        [
            'threshold' => 96_0000.0,
            'rate' => 0.35,
            'quick_deduction' => 8_5920.0,
        ],
        [
            'threshold' => null,
            'rate' => 0.45,
            'quick_deduction' => 18_1920.0,
        ],
    ];

    /**
     * 计算累计月份区间的累计应缴税款。
     *
     * @param float $taxableAmount           本次应税金额
     * @param int $occurrenceMonth           本次收入发生月份（1-12）
     * @param array $yearIncomeEvents        本年度发生的收入事件数组
     *
     * @return float
     */
    public function calculateAccumulatedTax(float $taxableAmount, int $occurrenceMonth, array $yearIncomeEvents): float
    {
        $this->assertValidOccurrenceMonth($occurrenceMonth);
        $monthlyIncomes = $this->buildMonthlyIncomeMap($occurrenceMonth, $yearIncomeEvents);
        $monthlyIncomes[$occurrenceMonth] = ($monthlyIncomes[$occurrenceMonth] ?? 0.0) + max(0.0, $taxableAmount);

        $startMonth = $this->findStartMonth($monthlyIncomes, $occurrenceMonth);
        if ($startMonth === null) {
            return 0.0;
        }
        $accumulatedIncome = 0.0;
        $accumulatedTax = 0.0;

        for ($month = $startMonth; $month <= $occurrenceMonth; $month++) {
            $income = $monthlyIncomes[$month] ?? 0.0;
            $accumulatedIncome += $income;

            $totalDeductions = $accumulatedIncome * 0.2;
            $monthsCount = ($month - $startMonth) + 1 - (($taxableAmount == 0) ? 1 : 0); //taxableAmount如果为0，说明是为了算之前的税，本月不算
            $totalAllowance = $monthsCount * 5_000.0;

            $taxableBase = max(0.0, $accumulatedIncome - $totalDeductions - $totalAllowance);
            $accumulatedTax = $this->calculateTaxByBracket($taxableBase);

        }

        return $accumulatedTax;
    }

    /**
     * @param array<int, float> $monthlyIncomes
     */
    private function findStartMonth(array $monthlyIncomes, int $occurrenceMonth): ?int
    {
        $lastOkMonth = $occurrenceMonth;
        for ($month = $occurrenceMonth - 1; $month >= 1; $month--) {
            $_income = ($monthlyIncomes[$month] ?? 0.0);
            if ($_income > 0) {
                $lastOkMonth = $month;
                continue;
            } else {
                break;
            }
        }

        return $lastOkMonth;
    }

    /**
     * @param array<int, array{month:int, amount:float}> $yearIncomeEvents
     *
     * @return array<int, float>
     */
    private function buildMonthlyIncomeMap(int $occurrenceMonth, array $yearIncomeEvents): array
    {
        $monthlyIncomes = array_fill(1, $occurrenceMonth, 0.0);

        foreach ($yearIncomeEvents as $event) {
            if (!is_array($event) || !isset($event['month'], $event['amount'])) {
                throw new InvalidArgumentException('yearIncomeEvents 中的每个元素必须包含 month 与 amount');
            }

            $month = (int) $event['month'];
            $amount = (float) $event['amount'];

            if ($month < 1 || $month > 12) {
                throw new InvalidArgumentException('收入事件的月份必须在 1 到 12 之间');
            }
            if ($month > $occurrenceMonth) {
                throw new InvalidArgumentException('收入事件的月份不能晚于当前发生月份');
            }

            $monthlyIncomes[$month] = ($monthlyIncomes[$month] ?? 0.0) + max(0.0, $amount);
        }

        return $monthlyIncomes;
    }

    private function assertValidOccurrenceMonth(int $occurrenceMonth): void
    {
        if ($occurrenceMonth < 1 || $occurrenceMonth > 12) {
            throw new InvalidArgumentException('发生月份必须在 1 到 12 之间');
        }
    }

    /**
     * @return array<string, float|null>
     */
    private function determineBracket(float $taxableAmount): array
    {
        foreach ($this->taxBrackets as $bracket) {
            $threshold = $bracket['threshold'];
            if ($threshold === null || $taxableAmount <= $threshold) {
                return $bracket;
            }
        }

        return end($this->taxBrackets);
    }

    /**
     * 根据累计应税金额以及税率表计算应缴税金。
     */
    public function calculateTaxByBracket(float $taxableAmount): float
    {
        if ($taxableAmount <= 0.0) {
            return 0.0;
        }

        $bracket = $this->determineBracket($taxableAmount);

        return max(
            0.0,
            $taxableAmount * $bracket['rate'] - $bracket['quick_deduction']
        );
    }
}
